﻿using Dlpad.Services;
using Microsoft.Data.SqlClient;
using Microsoft.IdentityModel.Tokens;
using Model;
using Model.Options;
using System.Data;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;

public class AuthService : IAuthService
{
    private const int SALT_SIZE = 16;      // 128位盐值
    private const int HASH_SIZE = 32;      // 256位哈希
    private const int ITERATIONS = 100000; // OWASP推荐迭代次数

   

    private readonly string _connectionString;
    private readonly IConfiguration _configuration;
    private readonly ILogger<AuthService> _logger;

    

    public AuthService(IConfiguration configuration, ILogger<AuthService> logger)
    {
        _configuration = configuration;
        _connectionString = configuration.GetConnectionString("DefaultConnection");
        _logger = logger;

       
    }

    

    /// <summary>
    /// 生成密码哈希（带随机盐）
    /// </summary>
    private string HashPassword(string password, bool isAlreadyHashed = true)
    {
        // 第1步：生成密码学安全的随机盐
        var salt = new byte[SALT_SIZE];// 16字节 = 128位
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(salt);// 填充随机字节
        }

        // 第2步：PBKDF2迭代哈希
        // 如果密码已经是前端哈希过的，我们将其作为"密码"进行二次哈希
        using var pbkdf2 = new Rfc2898DeriveBytes(
            password,// 输入密码（可能是前端哈希值）
            salt,// 随机盐
            ITERATIONS, // 100,000次迭代
            HashAlgorithmName.SHA256 // HMAC-SHA256
        );
        var hash = pbkdf2.GetBytes(HASH_SIZE);// 32字节输出

        // 第3步：组合格式: [盐值][哈希]
        var hashBytes = new byte[SALT_SIZE + HASH_SIZE];// 48字节
        Array.Copy(salt, 0, hashBytes, 0, SALT_SIZE);//复制盐值到数组开头
        Array.Copy(hash, 0, hashBytes, SALT_SIZE, HASH_SIZE);//复制哈希值到盐值后面

        // 第4步：Base64编码存储
        return Convert.ToBase64String(hashBytes);
    }


    /// <summary>
    /// 验证密码（固定时间比较防定时攻击）
    /// </summary>
    private bool VerifyPassword(string password, string storedHash, bool isAlreadyHashed = true)
    {
        try
        {
            // 解码存储的哈希
            var hashBytes = Convert.FromBase64String(storedHash);

            // 验证格式：盐(16字节) + 哈希(32字节) = 48字节
            if (hashBytes.Length != SALT_SIZE + HASH_SIZE)
            {
                _logger.LogWarning("[AUTH] 存储的哈希长度异常");
                return false;
            }

            // 提取盐值
            var salt = new byte[SALT_SIZE];
            Array.Copy(hashBytes, 0, salt, 0, SALT_SIZE);

            // 计算输入密码的哈希
            using var pbkdf2 = new Rfc2898DeriveBytes(
                password, // 输入密码（可能是前端哈希值）
                salt,
                ITERATIONS,
                HashAlgorithmName.SHA256
            );
            var computedHash = pbkdf2.GetBytes(HASH_SIZE);

            // 提取存储的哈希
            var storedHashBytes = new byte[HASH_SIZE];
            Array.Copy(hashBytes, SALT_SIZE, storedHashBytes, 0, HASH_SIZE);

            // 固定时间比较（防止定时攻击）
            return CryptographicOperations.FixedTimeEquals(computedHash, storedHashBytes);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "[AUTH] 密码验证失败");
            return false;
        }
    }

    public async Task<User> Register(RegisterRequest request)
    {
        // 验证密码格式（如果是明文，验证复杂度；如果是哈希值，只验证长度）
        if (!request.IsHashed)
        {
            ValidatePassword(request.Password); // 对明文密码进行复杂度验证
        }
        else
        {
            // 如果是前端哈希值，验证其格式（SHA-256哈希应该是64位十六进制字符串）
            if (string.IsNullOrEmpty(request.Password) || request.Password.Length != 64)
            {
                throw new ArgumentException("密码格式错误");
            }
        }

        // 第2步：PBKDF2哈希生成
        // 无论前端是否哈希，后端都进行PBKDF2哈希
        var passwordHash = HashPassword(request.Password, request.IsHashed);

        // 第3步：数据库写入（参数化防SQL注入）
        using var connection = new SqlConnection(_connectionString);
        await connection.OpenAsync();

        // 1.启动Serializable事务
        using var transaction = connection.BeginTransaction(IsolationLevel.Serializable);

        try
        {
            // 2.在事务内先执行唯一性检查
            using var checkCommand = new SqlCommand(
                "SELECT COUNT(1) FROM Users WHERE Username = @Username",
                connection,
                transaction);

            checkCommand.Parameters.AddWithValue("@Username", request.Username);

            var exists = (int)await checkCommand.ExecuteScalarAsync() > 0;

            if (exists)
            {
                await transaction.RollbackAsync();
                throw new InvalidOperationException($"用户名'{request.Username}'已被注册");
            }

            // 3.执行插入操作（带事务）
            using var insertCommand = new SqlCommand(
                @"INSERT INTO Users (Username, PasswordHash) 
          OUTPUT INSERTED.Id, INSERTED.Username, INSERTED.CreatedAt 
          VALUES (@Username, @PasswordHash)",
                connection,
                transaction
            );

            insertCommand.Parameters.AddWithValue("@Username", request.Username);
            insertCommand.Parameters.AddWithValue("@PasswordHash", passwordHash);

            //先读取数据，再提交事务
            User user = null;
            using (var reader = await insertCommand.ExecuteReaderAsync())
            {
                if (await reader.ReadAsync())
                {
                    user = new User
                    {
                        Id = reader.GetInt32("Id"),
                        Username = reader.GetString("Username"),
                        CreatedAt = reader.GetDateTime("CreatedAt"),
                    };
                }
            } // 这里reader会自动关闭

            if (user != null)
            {
                await transaction.CommitAsync();
                _logger.LogInformation("[AUTH] 用户注册成功(Serializable): {Username}", request.Username);
                return user;
            }
            else
            {
                await transaction.RollbackAsync();
                throw new Exception("注册失败：未能读取插入的数据");
            }
        }
        catch (SqlException ex) when (ex.Number == 2627) // 唯一约束冲突
        {
            await transaction.RollbackAsync();
            _logger.LogWarning("[AUTH] Serializable事务检测到用户名冲突: {Username}", request.Username);
            throw new InvalidOperationException($"用户名 '{request.Username}' 已被注册");
        }
        catch (Exception ex)
        {
            await transaction.RollbackAsync();
            _logger.LogError(ex, "[AUTH] Serializable事务注册失败");
            throw;
        }
    }

    public async Task<string> Login(LoginRequest request)
    {
        // 第1步：查询用户（参数化查询）
        var user = await GetUserByUsernameAsync(request.Username);
        if (user == null)
        {
            // 防止用户名枚举攻击 - 对输入进行模拟哈希计算
            if (request.IsHashed)
            {
                // 如果是前端哈希值，模拟一次PBKDF2计算
                HashPassword("dummy_password", true);
            }
            else
            {
                HashPassword("dummy_password", false);
            }

            _logger.LogWarning("[AUTH] 登录失败: 用户名不存在 {Username}", request.Username);
            throw new UnauthorizedAccessException("用户名或密码错误");
        }

        // 第2步：固定时间密码验证
        if (!VerifyPassword(request.Password, user.PasswordHash, request.IsHashed))
        {
            _logger.LogWarning("[AUTH] 登录失败: 密码错误 {Username}", request.Username);
            throw new UnauthorizedAccessException("用户名或密码错误");
        }

        // 第3步：生成JWT令牌
        _logger.LogInformation("[AUTH] 用户登录成功: {Username}", request.Username);
        return GenerateJwtToken(user);
    }

    private async Task<User> GetUserByUsernameAsync(string username)
    {
        using var connection = new SqlConnection(_connectionString);
        await connection.OpenAsync();

        using var command = new SqlCommand(
            "SELECT Id, Username, PasswordHash, CreatedAt FROM Users WHERE Username = @Username",
            connection);
        command.Parameters.AddWithValue("@Username", username);

        using var reader = await command.ExecuteReaderAsync();
        if (await reader.ReadAsync())
        {
            return new User
            {
                Id = reader.GetInt32("Id"),
                Username = reader.GetString("Username"),
                PasswordHash = reader.GetString("PasswordHash"),
                CreatedAt = reader.GetDateTime("CreatedAt")
            };
        }

        return null;
    }

    /// <summary>
    /// 验证明文密码复杂度（仅用于明文密码）
    /// </summary>
    private void ValidatePassword(string password)
    {
        if (string.IsNullOrWhiteSpace(password))
            throw new ArgumentException("密码不能为空");

        if (password.Length < 8)
            throw new ArgumentException("密码长度至少8位");

        if (!password.Any(char.IsUpper))
            throw new ArgumentException("密码必须包含大写字母");

        if (!password.Any(char.IsLower))
            throw new ArgumentException("密码必须包含小写字母");

        if (!password.Any(char.IsDigit))
            throw new ArgumentException("密码必须包含数字");

        if (!password.Any(ch => !char.IsLetterOrDigit(ch)))
            throw new ArgumentException("密码必须包含特殊字符");
    }

    //JWT生成
    public string GenerateJwtToken(User user)
    {
        //第1步：创建签名密钥
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));// 从配置文件读取
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);// 使用HS256算法 头部

        //第2步：构建JWT负载（Payload）
        var token = new JwtSecurityToken(
            issuer: _configuration["Jwt:Issuer"],// 颁发者 一般指后端 服务器端
            audience: _configuration["Jwt:Audience"], // 受众 一般指前端或客户端
            claims: new[]

            {
                new Claim(ClaimTypes.Name, user.Username),
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())
            },
            expires: DateTime.UtcNow.AddHours(2),// 过期时间（UTC）
            signingCredentials: creds);// 签名凭证
        //第3步：序列化为字符串
        return new JwtSecurityTokenHandler().WriteToken(token); //签名
        // 输出格式："eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. ..."
    }
}